//#include <algorithm>
#include <QSize>
#include <QBrush>
#include "GlobPref.h"
#include "DataModel.h"
#include "DataTable.h"
#include "ModTable.h"
#include "scImpl.h"

#define FCT "ModTable::ModTable"
ModTable::ModTable(PkType entityId, QObject * parent) :
	QAbstractTableModel(parent), dt(DataModel::get()->getTable(entityId)), rq(0, false), setup(true),
	transId(0), nVisCells(0), touched(false)
{
	connect(DataModel::get(), SIGNAL(dataChangedSig(unsigned long)), this, SLOT(mapRowsSl(unsigned long)));
	const ColDescs & attrs(dt.getPayload().attrs);
	for(unsigned int i(0); i<attrs.size(); ++i)
	{
		if(attrs.at(i).funct==AFUNCT_DATA ||
			attrs.at(i).funct==AFUNCT_FPK || attrs.at(i).funct==AFUNCT_FIK ||
			attrs.at(i).funct==AFUNCT_IKS || attrs.at(i).funct==AFUNCT_IKS2)
		{
			colMap.insert(ColMap::value_type(nVisCells, i));
			++nVisCells;
		}
	}
}

#undef FCT
#define FCT "ModTable::~ModTable"
ModTable::~ModTable()
{
	// TODO 2 bug - notify all views about deletion of model (due to garbage collection)
}

#undef FCT
#define FCT "ModTable::rowCount"
int ModTable::rowCount(const QModelIndex & parent) const
{
	//ctrc(Trc::LOOP,APP,FCT)<<rowMap.size()<<endl;
	return rowMap.size();
	Q_UNUSED(parent)
}

#undef FCT
#define FCT "ModTable::columnCount"
int ModTable::columnCount(const QModelIndex & parent) const
{
	return nVisCells;
	Q_UNUSED(parent)
}

#undef FCT
#define FCT "ModTable::data"
QVariant ModTable::data(const QModelIndex & index, int role) const
{
	// ctrc(Trc::LOOP,APP,FCT)<<"role "<<role<<endl;
	switch(role)
	{
	case Qt::DecorationRole:
	case Qt::CheckStateRole:
		return QVariant();
	case Qt::TextAlignmentRole:
		return QVariant(Qt::AlignLeft|Qt::AlignVCenter);
	case Qt::BackgroundRole:
	{
		// ctrc(Trc::LOOP,APP,FCT)<<"Qt::BackgroundRole col "<<index.column()<<endl;
		unsigned int colIdx(colMap.find(index.column())->second);
		if(dt.getPayload().attrs.at(colIdx).funct==AFUNCT_FIK)
			return QVariant(QBrush(QColor(248, 238, 255)));
		if(dt.getPayload().attrs.at(colIdx).funct==AFUNCT_FPK)
			return QVariant(QBrush(QColor(242, 244, 255)));
		return QVariant(QBrush(Qt::white));
	}
	default:
		break;
	}
	return QVariant(); // QAbstractTableModel::data(index, role) is abstract!
}

#undef FCT
#define FCT "ModTable::data(c)"
QString ModTable::data(int col, PkType pk, bool sortable) const
{
	unsigned int colIdx(colMap.find(col)->second);
	DOsCPkIdx::const_iterator dit(dt.getPayload().dos.get<DataObject::PkTag>().find(pk));
	if(dit==dt.getPayload().dos.get<DataObject::PkTag>().end())
	{
		DataModel::get()->dataMissed(dt.getPk(), pk);
		return tr("<missing data>");
	}
	const ColDesc & attr(dt.getPayload().attrs.at(colIdx));
	// TODO feature - cross-domain referencing
	if(attr.funct==AFUNCT_FPK)
	{
		const DataTable & dtdep(DataModel::get()->getTable(attr.target));
		const DataObject * dodep(dtdep.getByPk(dit->getPayload().at(colIdx).getDbKey()));
		if(!dodep)
		{
			DataModel::get()->dataMissed(dtdep.getPk(), dit->getPayload().at(colIdx).getDbKey());
			QString alt("[");
			alt=alt+QString().setNum(attr.target)+"PK"+QString().setNum(dit->getPayload().at(colIdx).getDbKey())+']';
			return alt;
		}
		return dodep->getPayload().at(dtdep.getPayload().niceNameCol).getAsQString(true);
	}
	else if(attr.funct==AFUNCT_FIK)
	{
		const DataTable & dtdep(DataModel::get()->getTable(attr.target));
		const DataObject * dodep(dtdep.getByIk(dit->getPayload().at(colIdx).getDbKey(), DataModel::get()->getDeadlineUTC()));
		if(!dodep)
		{
			DataModel::get()->dataMissed(dtdep.getPk(), dit->getPayload().at(colIdx).getDbKey(), DataModel::get()->getDeadlineUTC());
			QString alt("[");
			alt=alt+QString().setNum(attr.target)+"IK"+QString().setNum(dit->getPayload().at(colIdx).getDbKey())+']';
			return alt;
		}
		return dodep->getPayload().at(dtdep.getPayload().niceNameCol).getAsQString(true);
	}
	else if(attr.dtype==ADTYPE_DATIM)
	{
		if(sortable)
		{
			TimeType t(dit->getPayload().at(colIdx).getAsInt());
			return QString::number(t);
		}
		const QDateTime & val(dit->getPayload().at(colIdx).getQDateTime());
		if(val<=GlobPref::timeMinQ)
			return tr("long, long ago");
		else if(val>=GlobPref::timeMaxQ)
			return tr("far future");
		return val.toString(QString(DATETIME_USER));
	}
	else
		return dit->getPayload().at(colIdx).getAsQString(true);
}

#undef FCT
#define FCT "ModTable::headerData"
QVariant ModTable::headerData(int section, Qt::Orientation orientation, int role) const
{
	//ctrc(Trc::LOOP,APP,FCT)<<"section "<<section<<" orientation "<<orientation<<" role "<<role<<endl;
	switch(orientation)
	{
	case Qt::Horizontal:
	{
		switch(role)
		{
		case Qt::DisplayRole:
		{
			unsigned int colIdx(colMap.find(section)->second);
			return QVariant(dt.getPayload().attrs.at(colIdx).name);
		}
		case Qt::TextAlignmentRole:
			return QVariant(Qt::AlignHCenter|Qt::AlignVCenter);
		default:
			break;
		}
		break;
	}
	case Qt::Vertical:
	{
		switch(role)
		{
		case Qt::DisplayRole:
			return QVariant(section+1);
		case Qt::TextAlignmentRole:
			return QVariant(Qt::AlignLeft|Qt::AlignVCenter);
		default:
			break;
		}
		break;
	}
	}
	return QAbstractTableModel::headerData(section, orientation, role);
}

#undef FCT
#define FCT "ModTable::sort"
void ModTable::sort(int column, Qt::SortOrder order)
{
	Comparer cmp(this, column, order==Qt::AscendingOrder);
	// TODO 3 bug - remember sort order for next mapRows
	std::sort(rowMap.begin(), rowMap.end(), cmp);
	emit dataChanged(index(0, 0), index(rowCount(QModelIndex())-1, columnCount(QModelIndex())-1));
}

#undef FCT
#define FCT "ModTable::mapRowsSl"
void ModTable::mapRowsSl(unsigned long transactionId)
{
	// We ignore transId on purpose because the base class does not request
	// data, only derived classes with own request may filter.
	rowMap.clear();
	// base on primary key
	DOsCPkIdx & idx(dt.getPayload().dos.get<DataObject::PkTag>());
	int i(0);
	for(DOsCPkIdx::const_iterator it(idx.begin()); it!=idx.end(); ++it, ++i)
	{
		ctrc(Trc::LOOP,APP,FCT)<<"map row "<<i<<" to PK "<<it->getPk()<<endl;
		rowMap.push_back(it->getPk());
	}
	if(rowMap.empty())
		ctrc(Trc::LOOP,APP,FCT)<<"map row empty!"<<endl;
	emit dataChanged(index(0, 0), index(rowCount(QModelIndex())-1, columnCount(QModelIndex())-1));
	Q_UNUSED(transactionId)
}

#undef FCT
#define FCT "ModTable::mapRow2Pk"
PkType ModTable::mapRow2Pk(int row) const
{
	return rowMap.at(row);
}

#undef FCT
#define FCT "ModTable::mapRow2Ik"
PkType ModTable::mapRow2Ik(int row) const
{
	return dt.getPayload().dos.get<DataObject::PkTag>().find(mapRow2Pk(row))->getIk();
}

#undef FCT
#define FCT "ModTable::Comparer::op()"
bool ModTable::Comparer::operator()(PkType a, PkType b)
{
	// ctrc(Trc::LOOP,APP,FCT)<<"col "<<col<<" rowa "<<a<<" rowb "<<b<<endl;
	if(asc)
		return pr->data(col, a, true)<pr->data(col, b, true);
	else
		return pr->data(col, a, true)>pr->data(col, b, true);
}

#undef FCT
#define FCT "ModTable::setTouched"
void ModTable::setTouched()
{
	touched=true;
	DOsCPkIdx & didx(dt.getPayload().dos.get<DataObject::PkTag>());
	for(RowMap::iterator it(rowMap.begin()); it!=rowMap.end(); ++it)
	{
		DOsCPkIdx::const_iterator dit(didx.find(*it));
		if(dit!=didx.end())
			dit->getPayload().setTouched();
	}
	// TODO feature - Touch objects in dependent tables.
}
